/**
 * 
 */
package org.statewalker.parser.xhtml.validator;

import java.util.ArrayList;
import java.util.List;

import org.statewalker.dict.INamedToken;
import org.statewalker.dict.IToken;
import org.statewalker.parser.xhtml.grammar.XHTMLDict;
import org.statewalker.tokenizer.text.TextDict;
import org.statewalker.tokenizer.xml.XMLDict;
import org.statewalker.validator.IValidatorListener;
import org.statewalker.validator.Validator;
import org.statewalker.validator.ValidatorState;

public class XHTMLValidator extends Validator {

    public static class CompositeExtraTokenListener
        implements
        IExtraTokenListener {

        private List<IExtraTokenListener> fList = new ArrayList<IExtraTokenListener>();

        public void addListener(IExtraTokenListener listener) {
            fList.add(listener);
        }

        public void onToken(ValidatorState currentState, IToken token) {
            for (IExtraTokenListener listener : fList) {
                listener.onToken(currentState, token);
            }
        }

        public void removeListener(IExtraTokenListener listener) {
            fList.remove(listener);
        }

    }

    public interface IExtraTokenListener {

        void onToken(ValidatorState currentState, IToken token);

    }

    private boolean fAcceptSpaces;

    private CompositeExtraTokenListener fExtraListener = new CompositeExtraTokenListener();

    protected XHTMLValidatorFactory fFactory;

    public XHTMLValidator(XHTMLValidatorFactory factory) {
        super(factory.getGrammar());
        fFactory = factory;
        addListeners(new IValidatorListener() {

            public void onActivate(
                ValidatorState parent,
                ValidatorState state,
                IToken event) {
                setWhitespaces(state);
            }

            public void onDeactivate(
                ValidatorState parent,
                ValidatorState state,
                IToken event) {
                if (parent != null) {
                    setWhitespaces(parent);
                }
            }

            public void onTransition(
                ValidatorState parent,
                ValidatorState prev,
                IToken event,
                ValidatorState next) {
            }

            private void setWhitespaces(ValidatorState parent) {
                if (parent != null) {
                    String stateKey = parent.getKey();
                    fAcceptSpaces = fFactory.acceptSpaces(stateKey);
                }
            }
        });

    }

    public void addExtraTokenListener(IExtraTokenListener listener) {
        fExtraListener.addListener(listener);
    }

    @Override
    public void addListeners(IValidatorListener... listeners) {
        super.addListeners(listeners);
        for (IValidatorListener listener : listeners) {
            if (listener instanceof IExtraTokenListener) {
                addExtraTokenListener((IExtraTokenListener) listener);
            }
        }
    }

    @Override
    protected IToken filterToken(IToken token) {
        IToken result = null;
        String tokenKey = token.getKey();
        if (XMLDict.TAG.equals(tokenKey)) {
            String name = getTokenName(token);
            if (name != null) {
                name = name.toLowerCase();
                if (XHTMLDict._ALL_TAGS_SET.contains(name)) {
                    token.setKey(name);
                    result = token;
                } else {
                    ValidatorState currentState = fProcess.getCurrent();
                    fExtraListener.onToken(currentState, token);
                }
            }
        } else if (TextDict.SPACES.equals(tokenKey)
            || TextDict.EOL.equals(tokenKey)) {
            if (fAcceptSpaces) {
                result = token;
            } else {
                ValidatorState currentState = fProcess.getCurrent();
                fExtraListener.onToken(currentState, token);
            }
        } else if (XMLDict.COMMENT.equals(tokenKey)
            || XMLDict.PROCESSING.equals(tokenKey)
            || XMLDict.PROLOG.equals(tokenKey)) {
            ValidatorState currentState = fProcess.getCurrent();
            fExtraListener.onToken(currentState, token);
        } else {
            result = token;
        }
        return result;
    }

    protected String getTokenName(IToken token) {
        String result = null;
        if (token instanceof INamedToken) {
            result = ((INamedToken) token).getName();
        }
        return result;
    }

    public void removeExtraTokenListener(IExtraTokenListener listener) {
        fExtraListener.removeListener(listener);
    }

    @Override
    public void removeListeners(IValidatorListener... listeners) {
        super.removeListeners(listeners);
        for (IValidatorListener listener : listeners) {
            if (listener instanceof IExtraTokenListener) {
                removeExtraTokenListener((IExtraTokenListener) listener);
            }
        }
    }
}